package br.mikhas.util;

import java.util.ArrayList;
import java.util.regex.Pattern;

/**
 * Provides String utilities
 * 
 * @author Mikhail Domanoski
 * 
 */
public final class StringUtils {
	private static final String EMPTY = "";
	private static final Pattern pattern = Pattern.compile("\\p{Cntrl}");
	private static final char EXTENSION_SEPARATOR = '.';

	private StringUtils() {
	}

	/**
	 * Safe trim an string.
	 * <p>
	 * Theis method checks if the string is <tt>null</tt> before triming it
	 * 
	 * @param string
	 *            The string to be trimmed
	 * @return The trimmed string or <tt>null</tt>
	 */
	public static String trim(String string) {
		if (!isEmpty(string))
			return null;
		else
			return string.trim();
	}

	/**
	 * Capitalizes the first letter of a String.
	 * <p>
	 * Ex.: lower case => Lower case
	 */
	public static String capitalize(String string) {
		Assert.notEmpty(string);
		char[] charArray = string.toCharArray();
		charArray[0] = Character.toUpperCase(charArray[0]);
		return new String(charArray);
	}

	/**
	 * Uncapitalizes the first letter of a String
	 * <p>
	 * Ex.: UPPER CASE => uPPER CASE
	 */
	public static String uncapitalize(String string) {
		Assert.notEmpty(string);
		char[] charArray = string.toCharArray();
		charArray[0] = Character.toLowerCase(charArray[0]);
		return new String(charArray);
	}

	/**
	 * Capitalizes the first letter of all words on the string
	 * <p>
	 * Ex.: lower case => Lower Case
	 */
	public static String capitalizeAll(String string) {
		Assert.notEmpty(string);
		char[] charArray = string.toCharArray();
		charArray[0] = Character.toUpperCase(charArray[0]);

		if (charArray.length > 1) {

			boolean lastBlank = false;
			char c;
			for (int i = 1; i < charArray.length; i++) {
				c = charArray[i];

				if (c == ' ' || c == '\n') {
					lastBlank = true;
				} else if (lastBlank) {
					charArray[i] = Character.toUpperCase(c);
					lastBlank = false;
				}
			}
		}
		return new String(charArray);
	}

	/**
	 * Checks if the String is empty
	 * 
	 * @param string
	 *            The string to check
	 * @return returns <code>true</code> if the String is empty or
	 *         <code>null</code>
	 */
	public static boolean isEmpty(String string) {
		return string == null || string.isEmpty() || string.trim().isEmpty();
	}

	/**
	 * Encodes an String into an HTML-safe string
	 * 
	 * @param string
	 *            the string to be encoded
	 * @return an html-safe string
	 */
	public static String encodeHTML(String string) {
		StringBuilder builder = new StringBuilder((int) (string.length() * 1.1));
		encodeHTML(string, builder);
		return builder.toString();
	}

	/**
	 * Encodes an String into an HTML-safe string
	 * 
	 * @param string
	 *            the string to be encoded
	 * @return an html-safe string
	 */
	public static void encodeHTML(String string, StringBuilder builder) {
		int len = string.length();
		char c;

		for (int index = 0; index < len; index++) {
			c = string.charAt(index);
			if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0'
					&& c <= '9' || c == ' ') {
				// Safe character
				builder.append(c);
			} else if (c == '\n') {
				builder.append("\\n");
			} else if (Character.isWhitespace(c)) {
				builder.append("&#").append((int) c).append(';');
			} else if (Character.isISOControl(c)) {
			} else if (Character.isHighSurrogate(c)) {
				int codePoint;
				if (index + 1 < len
						&& Character.isSurrogatePair(c,
								string.charAt(index + 1))
						&& Character.isDefined(codePoint = (Character
								.toCodePoint(c, string.charAt(index + 1))))) {
					builder.append("&#").append(codePoint).append(";");
				}
				index++;
			} else if (Character.isLowSurrogate(c)) {
			} else {
				if (Character.isDefined(c)) {
					builder.append("&#").append((int) c).append(';');
				}
			}
		}
	}

	/**
	 * Splits the given string around the given <tt>char</tt>.
	 * <p>
	 * This method prevents of returning empty strings in the result array.
	 * <p>
	 * The single <tt>char</tt> split implementation is about 3.5x faster than
	 * the JDK RegExp split implementation.
	 * 
	 * @param string
	 *            The string to be splitted
	 * @param c
	 *            The delimiting <tt>char</tt>
	 * @return An array of strings with no empty strings
	 */
	public static String[] split(String string, char c) {
		return split(string, c, false);
	}

	/**
	 * Splits the given string around the given <tt>char</tt>.
	 * 
	 * <p>
	 * The single <tt>char</tt> split implementation is about 3.5x faster than
	 * the JDK RegExp split implementation.
	 * 
	 * @param string
	 *            The string to be splitted
	 * @param c
	 *            The delimiting <tt>char</tt>
	 * @param empty
	 *            flags if empty string should be added to the result
	 * @return An array of strings
	 */
	public static String[] split(String string, char c, boolean empty) {
		if (isEmpty(string))
			return new String[0];

		char[] chars = string.toCharArray();
		int from = 0, to = 0, len = string.length(), count;
		ArrayList<String> parts = new ArrayList<String>();

		while (to < len) {
			if (chars[to] == c) {
				count = to - from;
				// prevent empty string
				if (from != (to - 1))
					parts.add(new String(chars, from, count));
				else if (empty)
					parts.add(EMPTY);
				from = (++to);
			}
			to++;
		}
		count = to - from;

		// prevent empty string
		if (from != (to - 1))
			parts.add(new String(chars, from, count));
		else if (empty)
			parts.add(EMPTY);

		return parts.toArray(new String[parts.size()]);
	}

	/**
	 * Remove control characters from the given string
	 * 
	 * @param string
	 *            The string to remove the control characters
	 * @return a control-character-safe string
	 */
	public static String removeControl(String string) {
		return pattern.matcher(string).replaceAll(EMPTY);
	}

	/**
	 * Gets the extension from a file name
	 * 
	 * @param name
	 *            The file name
	 * @return the extension
	 */
	public static String getFileExtension(String name) {
		int lastIndex = name.lastIndexOf(EXTENSION_SEPARATOR);
		return name.substring(lastIndex + 1);
	}
}
